/*
 * @Author: huangwei
 * @Date: 2021-06-21 17:25:03
 * @LastEditors: 黄威
 * @LastEditTime: 2022-03-08 14:04:36
 * @Description: 地图视角操作相关工具（一些二三维数据转换）
 */

import type { View } from 'types'
import L from 'leaflet'
import * as Cesium from 'Cesium'

import { cartesianToLL } from './coordinate'

export type ViewCtrl = {
    getHeightFromZoom: (zoom: number) => number
    getZoomFromHeight: (height: number) => number
    zoomToTargetZoom: (targetZoom: number) => void
}

export interface Viewer extends Cesium.Viewer {
    vc: ViewCtrl
}

// 获取cesium指定位置坐标
export function viewerPix2Coord(
    viewer: Cesium.Viewer,
    pos: Cesium.Cartesian2,
    pickScean = false
) {
    if (viewer.scene.mode === Cesium.SceneMode.SCENE3D) {
        let result
        try {
            if (pickScean) {
                result = viewer.scene.pickPosition(pos)
            } else {
                result = viewer.scene.globe.pick(
                    viewer.camera.getPickRay(pos),
                    viewer.scene
                )
            }
        } catch (error) {
            return
        }

        // 不在图上
        if (!result) return

        return cartesianToLL(result)
    } else {
        const result = viewer.camera.pickEllipsoid(pos)
        // 不在图上
        if (!result) return

        return cartesianToLL(result)
    }
}

// 获取三维控制器
export function getViewCtrl(
    viewer: Cesium.Viewer,
    options: { scale?: number } = {}
): ViewCtrl {
    const scale = options.scale || 1

    const A = 40487.57
    const B = 0.00007096758
    const C = 91610.74
    const D = -40467.74

    // 从zoom中获取对应的高程
    function getHeightFromZoom(zoom: number): number {
        const height = Math.pow((A - D) / (zoom - D) - 1, 1 / B) * C
        return height * scale
    }

    // 从高程中获取对应zoom
    function getZoomFromHeight(height: number): number {
        height /= scale
        const zoom = Math.round(D + (A - D) / (1 + Math.pow(height / C, B)))
        return Math.max(0, zoom)
    }

    // 缩放到指定的zoom层级
    function zoomToTargetZoom(targetZoom: number) {
        const currentHeight = Math.floor(
            viewer.camera.positionCartographic.height
        )

        const targetHeight = getHeightFromZoom(targetZoom)
        const zoomOffset = currentHeight - targetHeight

        viewer.camera.zoomIn(zoomOffset)
    }

    return {
        getHeightFromZoom,
        getZoomFromHeight,
        zoomToTargetZoom,
    }
}

// 获取中心点和zoom
export function getView(target: L.Map | Cesium.Viewer): View | undefined {
    // 3d 获取中心点
    if (target instanceof Cesium.Viewer) {
        const result = viewerPix2Coord(
            target,
            new Cesium.Cartesian2(
                target.canvas.clientWidth / 2,
                target.canvas.clientHeight / 2
            )
        )
        // 不在图上
        if (!result) return

        const [lng, lat] = result
        const zoom = (target as Viewer).vc.getZoomFromHeight(
            target.camera.positionCartographic.height
        )

        return { center: [lng, lat], zoom: zoom }
    }
    // 2d 获取中心点
    else {
        try {
            const center = target.getCenter()
            const zoom = target.getZoom()
            const view: View = {
                center: [center.lng, center.lat],
                zoom,
            }
            return view
        } catch (error) {
            return {
                center: [0, 0],
                zoom: 0,
            }
        }
    }
}

// 比较两个view，true表示视图改变了
export function compareView(view1?: View, view2?: View): boolean {
    if (view1 && view2) {
        const flag = view1.zoom !== view2.zoom

        // zoom 改变
        if (flag) return true

        // 判断中心点改变（经纬度大于7位后数据精度可以忽略不记）
        return (
            Math.abs(view1.center[0] - view2.center[0]) >= 1e-7 ||
            Math.abs(view1.center[1] - view2.center[1]) >= 1e-7
        )
    }

    if (view1 && !view2) return true
    if (!view1 && view2) return true
    return false
}

// L or Cesium 数据转中心点
export function toCentoid(centoid: any): [number, number, number?] {
    let ll = centoid

    // L.LatLng
    if ('lng' in centoid && 'lat' in centoid) {
        ll = [centoid.lng, centoid.lat]

        if ('alt' in centoid) {
            ll[2] = centoid.alt || 0
        }
    }
    // Cesium.Cartesian3
    else if ('x' in centoid && 'y' in centoid) {
        ll = [centoid.x, centoid.y]

        if ('z' in centoid) {
            ll[2] = centoid.z || 0
        }
    }
    return ll
}

// 数据转boundary
export function toBoundary(extent: any): [number, number, number, number] {
    let minx, miny, maxx, maxy

    // L.LatLngBounds
    if (
        'getWest' in extent &&
        'getSouth' in extent &&
        'getEast' in extent &&
        'getNorth' in extent
    ) {
        minx = extent.getWest()
        miny = extent.getSouth()
        maxx = extent.getEast()
        maxy = extent.getNorth()
    }
    // Cesium.Rectangle
    else if (
        'west' in extent &&
        'south' in extent &&
        'east' in extent &&
        'north' in extent
    ) {
        minx = extent.west
        miny = extent.south
        maxx = extent.east
        maxy = extent.north
    }
    // [p1, p2] 两个顶点类型
    else if (Array.isArray(extent) && extent.length === 2) {
        const [x1, y1] = toCentoid(extent[0])
        const [x2, y2] = toCentoid(extent[1])
        minx = Math.min(x1, x2)
        miny = Math.min(y1, y2)
        maxx = Math.max(x1, x2)
        maxy = Math.max(y1, y2)
    } else {
        ;[minx, miny, maxx, maxy] = extent
    }

    return [minx, miny, maxx, maxy]
}
